BiblioAdmin model binding 1 op 1
Home

BiblioAdmin model binding 1 op 1

BiblioAdmin model binding 1 op 1

In deze les leren we hoe je het model en de view aanpast voor een property die een foreign-key is. Uitleg hierover vind je op Relationships (10/27/2016). Een praktisch voorbeeld: Saineshwar Bageri, Binding Dropdown List With Database In ASP.NET Core MVC, Dec 12 2016

Video

Probleem

  1. In de tabel Order zitten er een kolommen met een foreign-key, namelijk:
    1. CustomerId verwijst naar de Customer tabel
    2. ShippingMethodId verwijst naar de ShippingMethod tabel
    3. OrderStatusId verwijst naar de OrderStatus tabel
  2. In de tabel OrderItem zitten er ook een kolommen met een foreign-key, namelijk:
    1. OrderId verwijst naar de Order tabel
    2. BookId verwijst naar de Book tabel

Oplossing

Als voorbeeld nemen we Order.

  1. Wat gaat hieraan vooraf:
    1. Je hebt de CRUD pagina's van vorige les gemaakt.
    2. Als je daarmee nog problemen hebt, vind je een voorbeeldoplossing op mijn Bitbucket.
  2. Je hebt data ingevoerd in de tabel ShippingMethod, OrderStatus en Book.
  3. We beginnen met onszelf of een fictieve klant toe te voegen (gebruik daarvoor de InsertingOne pagina die we vorige week gemaakt hebben):
    InsertingOne Customer invoerformulier
    InsertingOne Customer invoerformulier
  4. We voegen een order toe zonder de foreign key waarden te tonen:
    1. We voegen data annotation toe om een kalender te tonen in het formulier voor de datums:
      [DataType(DataType.Date)]
      [Column(TypeName = "datetime")]
      public DateTime OrderDate { get; set; }
      [DataType(DataType.Date)]
      [Column(TypeName = "datetime")]
      public DateTime ShippingDate { get; set; }
    2. We runnen de web app en vullen het orderformulier in:
      order fk id letterlijk invoeren
      order fk id letterlijk invoeren

      Met dit als resultaat:

      order fk id letterlijk getoond
      order fk id letterlijk getoond
  5. In de Order/ReadingOne Razor Page willen we de namen van Customer, OrderStatus en ShippingMethod tonen en niet de Id:
    1. We passen de Bll/model klasse van Order aan. We voegen drie properties toe om de namen van de klanten, verzendmethoden en orderstatus bij te houden. Let op het gebruik van de data annotatie [NotMapped] om aan te geven dat die niet gebonden moeten worden aan het model wanneer deze waarden gepost worden.
      using System;
      using System.ComponentModel.DataAnnotations;
      using System.ComponentModel.DataAnnotations.Schema;
      
      namespace BiblioAdmin.Bll
      {
          public partial class Order
          {
      [DataType(DataType.Date)]
      [Column(TypeName = "datetime")]
      public DateTime OrderDate { get; set; }
      [DataType(DataType.Date)]
      [Column(TypeName = "datetime")]
      public DateTime ShippingDate { get; set; }
              [Column(TypeName = "varchar(512)")]
              public string Comment { get; set; }
              [Column(TypeName = "int(11)")]
              public int Id { get; set; }
              [Column(TypeName = "int(11)")]
              public int CustomerId { get; set; }
              [Column(TypeName = "int(11)")]
              public int ShippingMethodId { get; set; }
              [Column(TypeName = "int(11)")]
              public int StatusId { get; set; }
      
              [NotMapped]
              [ForeignKey("CustomerId")]
              public Customer Customer { get; set; }
      
              [NotMapped]
              [ForeignKey("ShippingMethodId")]
              public ShippingMethod ShippingMethod { get; set; }
      
              [NotMapped]
              [ForeignKey("StatusId")]
              public OrderStatus Status { get; set; }
          }
      }
      
      
    2. De namen kunnen Latijnse karakters bevatten zoals é, è enz. Om die correct uit de database in te lezen moeten we het model van Country aanpassen. Daarvoor heb je de MySql.Data.EntityFrameworkCore.DataAnnotations namespace nodig (MySQL Configuring Character Sets and Collations in EF Core):
      [MySqlCharset("latin1")]
      [NotMapped]
      [ForeignKey("CustomerId")]
      public Customer Customer { get; set; }
      
    3. In de Onget methode van de ReadingOneModel klasse in het Order/ReadingOne.cshtml.cs bestand, halen we de rijnen van de OrderStatus, OrderShipping en Customer tabel op en zetten ze in de overeenkomstige properties:
      public void OnGet(int? id)
      {
          this.Order = dbContext.Order.SingleOrDefault(m => m.Id == id);
          Order.ShippingMethod = dbContext.ShippingMethod.SingleOrDefault(m => m.Id == this.Order.ShippingMethodId);
          Order.Status = dbContext.OrderStatus.SingleOrDefault(m => m.Id == this.Order.StatusId);
          Order.Customer = dbContext.Customer.SingleOrDefault(m => m.Id == this.Order.CustomerId);
          OrderList = dbContext.Order.ToList();
      }
    4. In de ReadingOne view van Order vangen we het model op en tonen de naam van de ShippingMethod, OrderStatus en Customer in de HTML:
      <div>
          <label for="Order-Name">Klant</label>
          <input id="Order-Name" name="Order-Name" type="text" 
                 value="@Model.Order.Customer.FirstName @Model.Order.Customer.LastName" readonly>
      </div>
      <div>
          <label for="Order-Name">Verzendingsmethode</label>
          <input id="Order-Name" name="Order-Name" type="text" 
                value="@Model.Order.ShippingMethod.Name" readonly>
      </div>
      <div>
          <label for="Order-Name">Status</label>
          <input id="Order-Name" name="Order-Name" type="text" 
                value="@Model.Order.Status.Name" readonly>
      </div>
    5. Met dit als resultaat:
      order fk namen getoond
      order fk namen getoond
  6. Partial Page
    Zoals je hierboven ziet, moeten we de id's in de lijst onderaan nog vervangen door de respectievelijke namen. Maar we hebben die lijst op elke pagina van Order staan. I.p.v. elke pagina te wijzigen gaan we een nieuwe techniek leren, nameijk Partial Pages.
    1. Maak een map met de naam Order in de Pages/Shared map.
    2. Maak daarin een Razor Page met de naam _ReadingAll.cshtml.
    3. Verwijder de @Page directive omdat dit geen volwaardige Razor Page is en voeg een model toe van het type generieke lijst met instanties van de Bll Order klasse:
      ---@Page---
      model List<Bll.Order>
      @{
      }
    4. Verplaats de volgende html uit de Razor pages met de naam /Order/ReadingOne.cshtml, /Order/Index.cshtml, /Order/UpdatingOne.cshtml en /Order/InsertingOne.cshtml: en plaats die in /Shared/Order/_ReadingAll.cshtml:
      <aside class="list">
          <table class="list">
              <thead>
                  <tr>
                      <th>
                      </th>
                      <th>
                          Id
                      </th>
                      <th>
                          Besteldatum
                      </th>
                      <th>
                          Verzenddatum
                      </th>
                      <th>
                          Opmerking
                      </th>
                      <th>
                          Klant
                      </th>
                      <th>
                          Verzedingsmethode
                      </th>
                      <th>
                          Status
                      </th>
                  </tr>
              </thead>
              <tbody>
                  @foreach (var item in Model)
                  {
                      <tr>
                          <td>
                              <a href="Order/ReadingOne/@item.Id">Select</a> |
                          </td>
                          <td>
                              @item.Id
                          </td>
                          <td>
                              @item.OrderDate
                          </td>
                          <td>
                              @item.ShippingDate
                          </td>
                          <td>
                              @item.Comment
                          </td>
                          <td>
                              @item.CustomerId
                          </td>
                          <td>
                              @item.ShippingMethodId
                          </td>
                          <td>
                              @item.StatusId
                          </td>
                      </tr>
                  }
              </tbody>
          </table>
      </aside>
      
      
    5. In de de Razor pages met de naam /Order/ReadingOne.cshtml, /Order/Index.cshtml, /Order/UpdatingOne.cshtml en /Order/InsertingOne.cshtml: en plaats die in /Shared/Order/_ReadingAll.cshtml vervang je de html door de Partial taghelper en geef je de generieke lijst OrderList mee door:
      @page
      @model BiblioAdmin.Pages.Order.IndexModel
      @{
      }
      <h1>Order Index</h1>
      <a asp-page="./InsertingOne" asp-page-handler="Inserting">Inserting One </a>
      
      <partial name="Order/_ReadingAll" model="Model.OrderList"/>
      
  7. In de InsertingOne view willen we de lijst van de verzendmethoden, orderstus en klanten tonen een keuzelijst:
    1. In de Onget methode van de InsertingOneModel klasse in het Order/InsertingOne.cshtml.cs bestand lezen we verzendmethoden, orderstus en klanten in en geven die aan de view door met behulp van publieke properties:
      using System;
      using System.Collections.Generic;
      using System.Linq;
      using System.Threading.Tasks;
      using Microsoft.AspNetCore.Mvc;
      using Microsoft.AspNetCore.Mvc.RazorPages;
      
      namespace BiblioAdmin.Pages.Order
      {
          public class InsertingOneModel : PageModel
          {
              private readonly Bll.Docent2Context dbContext;
              // voeg constructor toe om geïnjecteerde DBContext 
              // te kunnen binnenkrijgen in deze klasse
              public InsertingOneModel(Bll.Docent2Context dbContext)
              {
                  this.dbContext = dbContext;
              }
      
              [BindProperty]
              public Bll.Order Order { get; set; }
              public List<Bll.Order> OrderList { get; set; }
              public List<Bll.Customer> CustomerList { get; set; }
              public List<Bll.OrderStatus> OrderStatusList { get; set; }
              public List<Bll.ShippingMethod> ShippingMethodList { get; set; }
      
              public void OnGet()
              {
                  OrderList = dbContext.Order.ToList();
                  CustomerList = dbContext.Customer.ToList();
                  OrderStatusList = dbContext.OrderStatus.ToList();
                  ShippingMethodList = dbContext.ShippingMethod.ToList();
              }
      
              public ActionResult OnPostInsert(Bll.Order order)
              {
                  if (!ModelState.IsValid)
                  {
                      // als er een foutief gegeven is ingetypt ga terug
                      // de pagina en toon de fout
                      return Page(); // return page, nog een nieuwe ingeven
                  }
                  // dbContext.Order.Update(order);
                  dbContext.Order.Add(order);
                  dbContext.SaveChanges();
                  // keer terug naar de index pagina van Order
                  return RedirectToPage("Index");
              }
          }
      }
      
      
    2. We passen de Order/InsertingOne view aan en lezen de option elementen uit de ShippingMethodList, OrderStatusList en CustomerList:
      @page
      @model BiblioAdmin.Pages.Order.InsertingOneModel
      @{
      }
      
      <h3>InsertingOne Order</h3>
      <hr />
      <br />
      <form method="post">
          <fieldset>
              <div>
                  <label asp-for="Order.OrderDate"></label>
                  <input asp-for="Order.OrderDate" />
              </div>
              <div>
                  <label asp-for="Order.ShippingDate"></label>
                  <input asp-for="Order.ShippingDate" />
              </div>
              <div>
                  <label asp-for="Order.Comment"></label>
                  <textarea asp-for="Order.Comment"></textarea>
              </div>
              <div>
                  <label asp-for="Order.CustomerId"></label>
                  <input asp-for="Order.CustomerId" />
                  <select id="Order_CustomerId" name="Order.CustomerId">
                      @foreach (var item in Model.CustomerList)
                      {
                          <option value="@item.Id">@item.FirstName @item.LastName</option>
                      }
                  </select>
              </div>
              <div>
                  <label asp-for="Order.ShippingMethodId"></label>
                  <input asp-for="Order.ShippingMethodId" />
                   <select id="Order_ShippingMethodId" name="Order.ShippingMethodId">
                      @foreach (var item in Model.ShippingMethodList)
                      {
                          <option value="@item.Id">@item.Name</option>
                      }
                  </select>
              </div>
              <div>
                  <label asp-for="Order.StatusId"></label>
                  <input asp-for="Order.StatusId" />
                  <select id="Order_StatusId" name="Order.StatusId">
                      @foreach (var item in Model.OrderStatusList)
                      {
                          <option value="@item.Id">@item.Name</option>
                      }
                  </select>
              </div>
          </fieldset>
          <div>
              <button type="submit" value="Insert" asp-page-handler="Insert">Insert</button>
          </div>
      </form>
      <partial name="Order/_ReadingAll" model="Model.OrderList" />
      
      
  8. In de Order/UpdatingOne view moeten we de keuzelijst ook opvullen met elementen uit de ShippingMethodList, OrderStatusList en CustomerList:
    . Maar er komt iets extra bij: in het keuzeveld moet de naam van de ingegeven verzendmehode, klant en orderstatus komen te staan:
    1. In de Onget methode van de UpdatingOneModel klasse in het Order/UpdatingOne.cshtml.cs bestand lezen we verzendmethoden, orderstus en klanten in en geven die aan de view door met behulp van publieke properties:
      using System;
      using System.Collections.Generic;
      using System.Linq;
      using System.Threading.Tasks;
      using Microsoft.AspNetCore.Mvc;
      using Microsoft.AspNetCore.Mvc.RazorPages;
      
      namespace BiblioAdmin.Pages.Order
      {
          public class UpdatingOneModel : PageModel
          {
              private readonly Bll.Docent2Context dbContext;
              // voeg constructor toe om geïnjecteerde DBContext 
              // te kunnen binnenkrijgen in deze klasse
              public UpdatingOneModel(Bll.Docent2Context dbContext)
              {
                  this.dbContext = dbContext;
              }
      
              [BindProperty]
              public Bll.Order Order { get; set; }
              public List<Bll.Order> OrderList { get; set; }
              public List<Bll.Customer> CustomerList { get; set; }
              public List<Bll.OrderStatus> OrderStatusList { get; set; }
              public List<Bll.ShippingMethod> ShippingMethodList { get; set; }
      
              public void OnGet(int? id)
              {
                  this.Order = dbContext.Order.SingleOrDefault(m => m.Id == id);
                  Order.ShippingMethod = dbContext.ShippingMethod.SingleOrDefault(m => m.Id == this.Order.ShippingMethodId);
                  Order.Status = dbContext.OrderStatus.SingleOrDefault(m => m.Id == this.Order.StatusId);
                  Order.Customer = dbContext.Customer.SingleOrDefault(m => m.Id == this.Order.CustomerId);
                  OrderList = dbContext.Order.ToList();
                  CustomerList = dbContext.Customer.ToList();
                  OrderStatusList = dbContext.OrderStatus.ToList();
                  ShippingMethodList = dbContext.ShippingMethod.ToList();
              }
              public ActionResult OnPostUpdate(Bll.Order order)
              {
                  if (!ModelState.IsValid)
                  {
                      // als er een foutief gegeven is ingetypt ga terug
                      // de pagina en toon de fout
                      return Page(); // return page
                  }
                  // dbContext.OrderStatus.Update(orderStatus);
                  dbContext.Order.Update(order);
                  dbContext.SaveChanges();
                  // keer terug naar de index pagina van OrderStatus
                  return RedirectToPage("Index");
              }
          }
      }
      
      
    2. We passen vervolgens de UpdatingOne view aan:
      @page "{id}"
      @model BiblioAdmin.Pages.Order.UpdatingOneModel
      @{
      }
      <h3>UpdatingOne Order</h3>
      <hr />
      <br />
      <form method="post">
          <fieldset>
              <div>
                  <label asp-for="Order.Id"></label>
                  <input asp-for="Order.Id" readonly />
              </div>
              <div>
                  <label asp-for="Order.OrderDate"></label>
                  <input asp-for="Order.OrderDate" />
              </div>
              <div>
                  <label asp-for="Order.ShippingDate"></label>
                  <input asp-for="Order.ShippingDate" />
              </div>
              <div>
                  <label asp-for="Order.Comment"></label>
                  <textarea asp-for="Order.Comment"></textarea>
              </div>
              <div>
                  <label asp-for="Order.CustomerId"></label>
                  <input asp-for="Order.CustomerId" />
                  <select id="Order-CustomerId" name="Order.CustomerId">
                      @foreach (var item in Model.CustomerList)
                      {
                          <option value="@item.Id"
                                  selected="@(item.Id == Model.Order.CustomerId ? true : false)">
                              @item.FirstName @item.LastName
                          </option>
                      }
                  </select>
              </div>
              <div>
                  <label asp-for="Order.ShippingMethodId"></label>
                  <input asp-for="Order.ShippingMethodId" />
                  <select id="Order-ShippingMethodId" name="Order.ShippingMethodId">
                      @foreach (var item in Model.ShippingMethodList)
                      {
                          <option value="@item.Id"
                                  selected="@(item.Id == Model.Order.ShippingMethodId ? true : false)">
                              @item.Name
                          </option>
                      }
                  </select>
              </div>
              <div>
                  <label asp-for="Order.StatusId"></label>
                  <input asp-for="Order.StatusId" />
                  <select id="Order-StatusId" name="Order.StatusId">
                      @foreach (var item in Model.OrderStatusList)
                      {
                          <option value="@item.Id"
                                  selected="@(item.Id == Model.Order.StatusId ? true : false)">
                              @item.Name
                          </option>
                      }
                  </select>
              </div>
          </fieldset>
          <div>
              <button type="submit" value="Update" asp-page-handler="Update">Update</button>
          </div>
      </form>
      <partial name="Order/_ReadingAll" model="Model.OrderList" />

Opdracht

  1. Maak zelf de 1 op1 voor OrderStatus, ShippingMethode en Customer zoals in het lesvoorbeeld.
  2. Maak een Partial page voor ReadingAll voor alle entiteiten, nl Order, Customer, OrderItem, ShippingMethod, Customer, OrderStatus en Book.
  3. Zorg ervoor de de 1 op 1 in tussen OrderItem en Book zo implementeert dat als je een orderitem toevoegt, van een lijst kan selecteren uit boeken.
    1. De foreign key voor OrderId geef je letterlijk in met de Id van de order waaraan je de OderItem wilt koppelen. In de volgende les leren we de 1 op n relatie tussen Order en OrderItem implemeren.
  4. Maak een lay-out pagina voor de Admin pagina's. Als voorbeeld van de lay-out verwijs ik naar: Fric-frac Wireframes Person.
  5. Stel enkele eenvoudige CSS regels op om de lay-out van de Admin pagina's gebruiksvriendelijk te maken. Maak gebruik van GRID en Flexbox om de hoofdlay-out van de pagina's te maken. Info over flex en grid:
    1. CSS Horizontale navigatie opmaak
    2. CSS lay-out met flexbox
    3. Webtechnologie Labo Les 9 Stappen om een volledig project te maken
  6. Deze opdracht maakt deel uit van de eindopdracht.

JI
2020-12-13 19:22:29